为51单片机打造带接收缓冲区的串口(Buffered Uart)
51单片机自带一个硬件串口模块,引脚位于P3.0(RXD)与P3.1(TXD),使用还算方便,但是依旧有些不足:
(1)串口的初始化比较复杂,要设置定时器,而定时器的值要手动计算;
(2)串口没有接收缓冲区,每次只能接收一个字节,如果不及时处理收到的数据,那么数据就会丢失;
我希望做一个模块,能够让51单片机的串口带上接收缓冲区的功能。在一个初始化函数中,可以方便的设置波特率和接收缓冲区。另外可以查询接收缓冲区中是否有数据,有则可以顺序读出。也可以发送数据,函数阻塞直到所有数据都发出(本来考虑过异步发送,但是现在发现很多场合还是需要同步发送)。
缓冲区最主要的功能就是为串口提供了异步能力,能够让程序更加专注于业务逻辑,而不是频繁地处理串口中断,把代码搞得一塌糊涂。
为了兼容各种常见的晶振频率,目前只支持这几个波特率:300、600、1200、2400、4800。
在编译之前,请根据目标机器的晶振频率修改buffered_uart.c中CLK_FREQ_100HZ宏的值。CLK_FREQ_100HZ的值是晶振频率(单位是Hz)的百分之一。
比如用的是12Mhz的晶振,那么就应该:
+++code
#define CLK_FREQ_100HZ 120000
---code
如果用的是11.0592Mhz的晶振,那么就应该:
+++code
#define CLK_FREQ_100HZ 110592
---code
另外,串口是需要使用timer1定时器和interrupt 4中断的!注意避免冲突!
OK,直接上代码:
buffered_uart.h:
+++code
/*
该文件用来实现一个带有缓冲机制的串口
定义了方法

void uart_init(uint16 p_baud,uint8* p_buffer,uint8 p_capacity);
初始化串口,模式为8-n-1(8数据位,无校验位,1停止位),参数为
波特率、缓存、缓存容量

void uart_close();
关闭串口,释放占用的资源(缓冲区、定时器1)

uint8 uart_available();
获取缓存中以接收但还未读取的字节数量

uint8 uart_read();
从缓存中读取一个字节

bool uart_is_overflow();
判断缓存是否溢出(由于没有及时读取)

void uart_overflow_reset();
清除缓存溢出标志位

void uart_write(uint8 p_data);
发送一个字节

void uart_print(char* p_string);
发送一个字符串

void uart_println(char* p_string);
发送一个字符串并换行

目前只支持常用波特率300、600、1200、2400、4800
*/

#ifndef BUFFERED_UART_H
#define BUFFERED_UART_H

#include "types.h"

void uart_init(uint16 p_baud,uint8* p_buffer,uint8 p_capacity);
void uart_close();
uint8 uart_available();
uint8 uart_read();
bool uart_is_overflow();
void uart_overflow_reset();
void uart_write(uint8 p_data);
void uart_print(char* p_string);
void uart_println(char* p_string);

#endif
---code
buffered_uart.c
+++code
/*
buffered_uart.h的实现文件
请务必明确单片机的时钟频率,并相应修改CLK_FREQ_100HZ
CLK_FREQ_100HZ是晶振频率的百分之一
*/

#define CLK_FREQ_100HZ 120000

#include "buffered_uart.h"
#include <reg51.h>

#define INTERRUPT_NO 4

typedef struct
{
    uint8* buffer;
    uint8 max;
    uint8 start;
    uint8 len;
}
queue;

static uint8 queue_read(queue* p_queue)
{
    uint8 t_data;
    if(p_queue->len==0)
        return 0;
    t_data=p_queue->buffer[p_queue->start];
    ++p_queue->start;
    --p_queue->len;
    if(p_queue->start==p_queue->max)
        p_queue->start=0;
    return t_data;
}

static void queue_write(queue* p_queue,uint8 p_data)
{
    uint8 t_pos,t_rest;
    if(p_queue->len==p_queue->max)
        return;
    t_rest=p_queue->max-p_queue->len;
    if(p_queue->start<t_rest)
        t_pos=p_queue->start+p_queue->len;
    else
        t_pos=p_queue->start-t_rest;
    p_queue->buffer[t_pos]=p_data;
    ++p_queue->len;
}

static queue sg_recv;
bool sg_overflow;
bool sg_sending;

static void on_uart_interrupt() interrupt INTERRUPT_NO
{
    if(RI==1)
    {
        if(sg_recv.len==sg_recv.max)
            sg_overflow=1;
        else
            queue_write(&sg_recv,SBUF);
        RI=0;
    }
    else if(TI==1)
    {
        sg_sending=0;
        TI=0;
    }
}

void uart_init(uint16 p_baud,uint8* p_buffer,uint8 p_capacity)
{
    uint8 t_timer=0;
    #define SWITCH_TIMER(baud)
        if(p_baud==baud)
            t_timer=CLK_FREQ_100HZ/192*100/baud;
    SWITCH_TIMER(300)
    SWITCH_TIMER(600)
    SWITCH_TIMER(1200)
    SWITCH_TIMER(2400)
    SWITCH_TIMER(4800)
    SCON=0x50;//8-n-1
    PCON|=0x80;//SMOD=1,倍频
    TMOD&=0x0F;
    TMOD|=0x20;//这两句,把TMODE高4为置为0020,即timer1工作在模式2
    TH1=TL1=256-t_timer;
    TR1=1;
    PS=1;
    ES=1;
    EA=1;
    sg_recv.buffer=p_buffer;
    sg_recv.max=p_capacity;
    sg_recv.start=0;
    sg_recv.len=0;
    sg_overflow=0;
    sg_sending=0;
}

void uart_close()
{
    TR1=0;
    ES=0;
}

uint8 uart_available()
{
    return sg_recv.len;
}

uint8 uart_read()
{
    uint8 t_data;
    ES=0;
    t_data=queue_read(&sg_recv);
    ES=1;
    return t_data;
}

bool uart_is_overflow()
{
    return sg_overflow;
}

void uart_overflow_reset()
{
    ES=0;
    sg_overflow=0;
    ES=1;
}

void uart_write(uint8 p_data)
{
    while(sg_sending);
    ES=0;
    sg_sending=1;
    SBUF=p_data;
    ES=1;
}

void uart_print(char* p_string)
{
    while(*p_string)
    {
        uart_write(*p_string);
        ++p_string;
    }
}

void uart_println(char* p_string)
{
    uart_print(p_string);
    uart_print("rn");
}
---code
里面用到的types.h:
+++code
#ifndef TYPES_H
#define TYPES_H

typedef bit bool;
typedef char int8;
typedef unsigned char uint8;
typedef int int16;
typedef unsigned int uint16;

#endif
---code